package Cluedo.Tools;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Hashtable;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

import Cluedo.API.MapArea;
import Cluedo.API.MapLoader;

/**
 * @author Alex
 */
public class Adjacency extends JPanel implements MouseMotionListener, MouseListener{

	static ImageIcon img;
	static ArrayList areas;
	int x,y,i=0;
	int w,h;
	
	Color textColour = new Color(0,0,0,255); // (this is black)
//	Color textColour = new Color(255,255,255,255); // (this is white)
	Color lineColour = new Color(0,0,0,200);

	boolean debug = false;
	
	MapArea selected = null;
	MapArea target = null;
	
	Hashtable adjacencies = new Hashtable();  

	MouseEvent startPoint = null;
	MouseEvent endPoint = null;

	BufferedImage buff;

	private boolean painted = false;
	
	private boolean showLinks = true;
	
	static String mapName = "classic";

	
	AdjacencyToolbar at;
	
	
	synchronized public BufferedImage initaliseBuffer(ImageIcon img) {
		BufferedImage buff = new BufferedImage(img.getIconWidth(), img.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
		return buff;
	}
	
	synchronized public BufferedImage updateBuffer(ImageIcon img) {
		BufferedImage buff = this.buff;

		long start = System.currentTimeMillis();		
		
		Graphics g = buff.getGraphics();
		g.drawImage(img.getImage(),0,0,null);

		if(selected != null) {
		    Graphics2D g2 = ((Graphics2D)g);
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			
			g2.setColor(new Color(0,0,0,80));
			g2.fill(selected.getShape());
			
			g2.setColor(new Color(0,0,0,255));
			g2.draw(selected.getShape());
		}
		long end = System.currentTimeMillis();
		if(debug == true)
		    System.out.println("Buffer update: " + (end-start));
		
		return buff;

	}
	
	public Adjacency() {
		String fileName = (new File("./Resources/Maps/"+mapName+".gif")).getAbsolutePath();
		
		
		img = new ImageIcon(fileName);
		
		w = img.getIconWidth();
		h = img.getIconHeight();
		
		buff = initaliseBuffer(img);
		buff = updateBuffer(img);
		println(new File(fileName).exists()+"");

		Viewer viewer = new Viewer(img);
		
		setPreferredSize(new Dimension(img.getIconWidth(),img.getIconHeight()) );
		setOpaque(false);
		setBackground(Color.blue);
		setBounds(0,0,img.getIconWidth(),img.getIconHeight());
		add(viewer);

		dirtyLoad((new File("./Resources/Maps/"+mapName+".map")).getAbsolutePath());
	}


	/**
     * @param absolutePath
     */
    private void dirtyLoad(String absolutePath) {
        BufferedReader in = null;
        try {
            in = new BufferedReader(new FileReader(absolutePath));
        }catch(Exception e) {
            e.printStackTrace();
            return;
        }
        try {
            String ln = "";
            String parts[] = {};
            String links[] = {};
            Node n = null;

            while((ln = in.readLine())!=null) {
                if(ln.startsWith("#") || ln.length() == 0)
                    continue;
                
                parts = ln.split(":");
                links = parts[2].split(",");

                // look in hash table
                n = (Node)adjacencies.get(parts[0]);
                
                if(n == null) {
                    n = new Node(parts[0]);
                    adjacencies.put(n.ID, n);
                    if(!parts[1].equals("Square"))
                            n.type=parts[1];
                }
                for(int i=0;i<links.length;i++) {
                    n.addLink(links[i]);
                }
                
            }
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }

    public void paint( Graphics g ) {

		Graphics2D g2 = ((Graphics2D)g);
		
		i++;
		long end = System.currentTimeMillis();
		g2.drawImage(buff, 0,0,null);
		painted = true;

		if(target!=null) {
			int off = 2;
			
			g2.setColor(new Color(130,0,120,30));
			g2.fill(target.getShape());
			g2.setColor(lineColour);
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			g2.drawLine(startPoint.getX(), startPoint.getY(), endPoint.getX(), endPoint.getY());

			g2.drawRect(startPoint.getX()-off, startPoint.getY()-off, 5,5);
			g2.drawRect(endPoint.getX()-off, endPoint.getY()-off,5,5);
			
			g2.setColor(textColour);
			g2.drawString("Link to " + target.getID(), endPoint.getX(), endPoint.getY());
			
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
		}else {
		    if(showLinks == true && selected != null) {
		        long start = System.currentTimeMillis();
		        Node n =(Node) adjacencies.get(selected.getID());
		        if(n !=null) {
		            ArrayList N = n.links;
		            //g2.setColor(new Color(0,0,255,50));
		            g2.setColor(new Color(255,255,0,100));
		            for(int i=0;i<N.size(); i++ ) {
		                int index = findIndex((String)N.get(i));
		                if(index>-1)
		                    g2.fill(((MapArea)areas.get(index)).getShape());
		            }
		        }
		        long endtime = System.currentTimeMillis();
		        if(debug == true)
		            System.out.println("Painting took: " +(endtime-start) + " (ms)");

		    }
		    
		}
		
		super.paint(g);
		
		long start = System.currentTimeMillis();
		
		if((start-end) > 2)
			printlnerr("paint: "+(start-end)+ " (ms)");
		else
			println("paint: "+(start-end)+ " (ms)");
	}
	
	public static void main(String[] args) throws Exception {
		if(args.length == 1)
			mapName = args[0];

		MapLoader loader = new MapLoader();
		loader.loadGame("Resources/Maps/"+mapName+".xml");
		areas = loader.getMapAreas();
		
		JFrame frame = new JFrame();
		
		JFrame toolFrame = new JFrame();
		
		Adjacency a = new Adjacency();
		AdjacencyToolbar at = new AdjacencyToolbar(a);
		a.setToolbar(at);
		
		frame.getRootPane().add(a,0);
//		frame.getRootPane().add(a.label(), 1);
		frame.setVisible(true);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(img.getIconWidth(),img.getIconHeight());


		toolFrame.getContentPane().add(at);
		toolFrame.setVisible(true);
		toolFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//toolFrame.setSize(img.getIconWidth(),img.getIconHeight());
		toolFrame.pack();
		toolFrame.setVisible(true);
		
	}


	/**
	 * @param at
	 */
	private void setToolbar(AdjacencyToolbar at) {
		this.at = at;
		
	}

	/**
	 * @return
	 */
	private Component label() {
		JButton printOut = new JButton("Print to console");
		printOut.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Object[] n = adjacencies.keySet().toArray();

				System.out.println("Payload length: "+n.length+ "\n\n");

				for(int i =0; i< n.length; i++) {
					try {

						Adjacency.Node u = (Adjacency.Node)adjacencies.get((String)n[i]);
						System.out.println(u.ID+":"+u.type+":"+u.toString());
					}
					catch(ClassCastException exception) {

						exception.printStackTrace();

					}
				}
			}
		});
		
		printOut.setBounds(10,10,100,100);
		
		return printOut;
	}
	
	class Viewer extends JComponent {
		ImageIcon img;
		int nx=0,ny=0;

		private Viewer(ImageIcon img) {
			this.img=img;
			setPreferredSize(new Dimension(img.getIconWidth(), img.getIconHeight()));
			x=0;y=0;
			addMouseMotionListener(Adjacency.this);
			addMouseListener(Adjacency.this);

			setBackground(Color.white);
			setOpaque(true);
		}
		
		
		
		public void paintComponent( Graphics g ) {

			g.setColor(Color.black);
			g.drawString("I'm painting myself",10,10);
			println("Painting dynamic thing " + i); //+ img);
			//g.drawRect(x-nx,y-ny, 20,20);

			i++;
			
		}

	}
	
	public void println(String msg) {
		if(debug == true)
			System.out.println(msg);
	}
	public void printlnerr(String msg) {
		if(debug == true)
			System.err.println(msg);
	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
	 */
	public void mouseDragged(MouseEvent e) {

		x=e.getX();
		y=e.getY();
		if(selected == null)
			return;

		endPoint= e;
/*
		if(queue.size() >70)
			return;
	*/	
		long start = System.currentTimeMillis();
		int index = findHit(x,y);
		if(index >= 0) {
			target = (MapArea)areas.get(index);
			if(target == selected) {
				target = null;
			}
		}
		else
			target = null;
		
		if(target!= null) {
			setToolTipText("Link to: "+target.getID());
			repaint();
		}

		long end = System.currentTimeMillis();
		
	}



	/* (non-Javadoc)
	 * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
	 */
	

	
	public void mouseMoved(MouseEvent e) {
		/*x=e.getX();
		y=e.getY();
		
	
		if(queue.size() >70)
			return;
		
		long start = System.currentTimeMillis();
		int index = findHit(x,y);
		long end = System.currentTimeMillis();
		println("\t\tSearch " + (end- start)+"(ms) index=" + index);
		if(index >-1)
			queue.add(new Fader((MapArea) areas.get(index)));*/
	}
	synchronized private int findIndex(String name) {
		int i =-1;
		boolean found = false;
		for(i=0 ;i< areas.size();i++) {
			if(((MapArea)areas.get(i)).getID().equals(name)) {
				found = true;
				break;
			}
		}
		if(found==false)
		    i=-1;

		return i;
	}
	/**
	 * @param x2
	 * @param y2
	 * @return
	 */
	synchronized private int findHit(int x2, int y2) {
		int i =-1;
		boolean found = false;
		for(i=0 ;i< areas.size();i++) {
			if(((MapArea)areas.get(i)).getShape().contains(x2,y2)) {
				found = true;
				break;
			}
		}
		if(found == false)
			i=-1;
		/*
		
		if(i > -1) {
			for(int j =0;j < queue.size();j++)
				if(((Fader)queue.get(j)).map.getShape().contains(x2,y2)) {
					i=-1;
					break;
				}
		}
		*/
		
		return i;
	}

	class Fader {
		MapArea map;
		int alpha;
		Fader(MapArea a) {
			this.map=a;
			alpha = 200;
		}
		
		boolean decrement() {

			return ((alpha-=10) > 10);
		}
		
	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	public void mouseClicked(MouseEvent e) {
		startPoint = e;
		selected= null;
		target = null;
		
		if(e.getButton() == MouseEvent.BUTTON3){

			selected = null;
			target=null;

			updateBuffer(img);
			repaint();
			return;
		}

		int index = findHit(e.getX(), e.getY());
		if(index == -1)
			return;
		MapArea clicked = (MapArea)areas.get(index);
	/*	if(selected != null)
			System.out.println(selected.getID());
		else
			System.out.println(selected);*/
		
		selected = clicked;
		updateBuffer(img);
		this.at.updateFields();

		repaint();
		
	}

	public void mouseEntered(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {}
	public void mousePressed(MouseEvent e) {}

	public void mouseReleased(MouseEvent e) {
		if(target != null && selected != null && e.getButton() == MouseEvent.BUTTON1) {
			System.out.println("Adding link: " + selected.getID() + " <-> " + target.getID());
			Node existing = (Node)adjacencies.get(selected.getID());
			
			if(existing == null) {
				existing = new Node(selected.getID());
				adjacencies.put(selected.getID(), existing);
			}
			existing.addLink(target);

			// Make the relationship two-way, A->B implies B->A
			existing = (Node)adjacencies.get(target.getID());
			
			if(existing == null) {
				existing = new Node(target.getID());
				adjacencies.put(target.getID(), existing);
			}
			existing.addLink(selected);
			this.at.updateFields();
		}
	}
	
	public class Node {
		ArrayList links;
		String ID, type="Square";
		String startingPosFor = null;

		public Node(String ID) {
			links = new ArrayList();
			this.ID = ID;
		}
		
		public String toString() {
			StringBuffer buffer = new StringBuffer();
			for(int i=0;i<links.size();i++) {
				buffer.append((String)links.get(i)+",");
			}
			
			return buffer.toString();
		}

		
		public void addLink(String arg) {
			for(int i=0;i<links.size();i++)
				if(((String)links.get(i)).equals(arg)) {
					System.out.println("Not adding duplicate node");
						return;
				}
			links.add(arg);
		}		
		public void addLink(MapArea selected) {
		    addLink(selected.getID());
		}
	}

	/**
	 * @return node currently selected
	 */
	public Node getSelectedNode() {
		if(selected == null)
			return null;
		
		Node existing = (Node) adjacencies.get(selected.getID());
		if(existing == null) {
			existing = new Node(selected.getID());
			adjacencies.put(selected.getID(), existing);

		}

		return existing;
	}
}
